#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <semaphore.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/stat.h>

/*========== 宏定义 ==========*/
#define MYFIFO "myfifo"           // 命名管道的路径
#define BUFFER_SIZE 3             // 缓冲区槽位数量（信号量容量）
#define UNIT_SIZE 5               // 每次读写的数据单元大小（字节）
#define RUN_TIME 20               // 程序总运行时间（秒）
#define DELAY_TIME_LEVELS 5.0     // 随机延迟时间基数

/*========== 全局变量 ==========*/
int fd; // FIFO文件描述符
time_t end_time; // 程序结束时间戳
sem_t mutex, full, avail; // 同步信号量
// mutex: 互斥锁（保证缓冲区操作的原子性）
// full:  已填充槽位计数器（初始为0，消费者依赖）
// avail: 可用槽位计数器（初始为BUFFER_SIZE，生产者依赖）

/*========== 生产者线程 ==========*/
void *producer(void *arg)
{
    int real_write; // 实际写入字节数
    int delay_time = 0; // 随机延迟时间

    while (time(NULL) < end_time)
    {
        // 在运行时间内循环
        // 计算随机延迟（1~3秒）
        delay_time = (int) (rand() * DELAY_TIME_LEVELS / (RAND_MAX) / 2.0) + 1;
        sleep(delay_time);

        /*-- 进入临界区 --*/
        sem_wait(&avail); // 等待可用槽位（avail>0时继续，否则阻塞）
        sem_wait(&mutex); // 获取互斥锁

        printf("\nProducer: delay=%d\n", delay_time);

        // 尝试向FIFO写入数据
        if ((real_write = write(fd, "hello", UNIT_SIZE)) == -1)
        {
            if (errno == EAGAIN)
            {
                // FIFO已满的非阻塞错误
                printf("The FIFO has not been read yet. Please try later\n");
            }
        } else
        {
            printf("Write %d to the FIFO\n", real_write);
        }

        /*-- 离开临界区 --*/
        sem_post(&full); // 增加已填充槽位
        sem_post(&mutex); // 释放互斥锁
    }
    pthread_exit(NULL);
}

/*========== 消费者线程 ==========*/
void *customer(void *arg)
{
    // 随机延迟时间

    while (time(NULL) < end_time)
    {
        char read_buffer[UNIT_SIZE];
        // 在运行时间内循环
        // 计算随机延迟（1~5秒）
        const int delay_time = (int) (rand() * DELAY_TIME_LEVELS / (RAND_MAX)) + 1;
        sleep(delay_time);

        /*-- 进入临界区 --*/
        sem_wait(&full); // 等待已填充数据（full>0时继续，否则阻塞）
        sem_wait(&mutex); // 获取互斥锁

        memset(read_buffer, 0, UNIT_SIZE); // 清空缓冲区
        printf("\nCustomer: delay=%d\n", delay_time);

        // 尝试从FIFO读取数据
        const int real_read = read(fd, read_buffer, UNIT_SIZE);
        if (real_read == -1)
        {
            if (errno == EAGAIN)
            {
                // FIFO为空的非阻塞错误
                printf("No data yet\n");
            }
        } else
        {
            printf("Read %s from FIFO\n", read_buffer);
        }

        /*-- 离开临界区 --*/
        sem_post(&avail); // 增加可用槽位
        sem_post(&mutex); // 释放互斥锁
    }
    pthread_exit(NULL);
}

/*========== 主函数 ==========*/
int main()
{
    pthread_t thrd_prd_id, thrd_cst_id; // 线程ID
    // 通用返回值检查

    srand(time(NULL)); // 初始化随机数种子
    end_time = time(NULL) + RUN_TIME; // 设置结束时间戳

    /*-- 创建命名管道 --*/
    // 权限模式0666：用户、组、其他都有读写权限
    // 创建FIFO并显式设置权限
    if (mkfifo(MYFIFO, 0666) == -1)
    {
        if (errno != EEXIST)
        {
            perror("Cannot create fifo");
            exit(EXIT_FAILURE);
        }
    }
    // 显式修改权限（覆盖umask设置）
    if (chmod(MYFIFO, 0666) == -1)
    {
        perror("chmod failed");
        exit(EXIT_FAILURE);
    }

    /*-- 打开FIFO --*/
    // 使用O_RDWR模式避免读写阻塞（某些系统可能不支持）
    // 生产者和消费者通常应分别以O_WRONLY和O_RDONLY打开
    fd = open(MYFIFO, O_RDWR);
    if (fd == -1)
    {
        perror("Open fifo error");
        exit(EXIT_FAILURE);
    }

    /*-- 初始化信号量 --*/
    int ret = sem_init(&mutex, 0, 1); // 互斥锁初始为1（未锁定）
    ret += sem_init(&avail, 0, BUFFER_SIZE); // 可用槽位数=缓冲区大小
    ret += sem_init(&full, 0, 0); // 已填充槽位初始为0
    if (ret != 0)
    {
        perror("Semaphore initialization failed");
        exit(EXIT_FAILURE);
    }

    /*-- 创建生产者线程 --*/
    if (pthread_create(&thrd_prd_id, NULL, producer, NULL) != 0)
    {
        perror("Create producer thread error");
        exit(EXIT_FAILURE);
    }

    /*-- 创建消费者线程 --*/
    if (pthread_create(&thrd_cst_id, NULL, customer, NULL) != 0)
    {
        perror("Create customer thread error");
        exit(EXIT_FAILURE);
    }

    /*-- 等待线程结束 --*/
    pthread_join(thrd_prd_id, NULL); // 阻塞直到生产者线程结束
    pthread_join(thrd_cst_id, NULL); // 阻塞直到消费者线程结束

    /*-- 清理资源 --*/
    close(fd); // 关闭FIFO文件描述符
    unlink(MYFIFO); // 删除命名管道文件
    return 0;
}

/*========== 执行流程说明 ==========
1. 创建命名管道（持久化存在于文件系统）
2. 以读写模式打开管道（非阻塞方式）
3. 初始化三个信号量：
   - mutex：二进制信号量，保护临界区
   - avail：表示可用缓冲区槽位，初始为BUFFER_SIZE
   - full： 表示已用缓冲区槽位，初始为0
4. 创建生产者线程：
   - 随机延迟后尝试获取可用槽位
   - 写入固定内容"hello"
   - 更新full信号量
5. 创建消费者线程：
   - 随机延迟后尝试获取已填充槽位
   - 读取管道内容
   - 更新avail信号量
6. 主线程等待两个子线程结束后，关闭并删除管道
=================================*/
